package com.zlsy.thread.lock.reentrantlock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author zhouliang
 * @date 2020/8/5 20:33
 * 演示ReentrantReadWriteLock头节点不是写锁就可以插队的现象
 */
public class NofairBargeDemo {

    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(false);

    private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

    private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

    private static void read() {
        System.out.println(Thread.currentThread().getName() + "开始尝试获取读锁！");
        readLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "得到读锁，正在读取。");
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            readLock.unlock();
            System.out.println(Thread.currentThread().getName() + "释放读锁！");
        }
    }


    private static void write() {
        System.err.println(Thread.currentThread().getName() + "开始尝试获取写锁！");
        writeLock.lock();
        try {
            System.err.println(Thread.currentThread().getName() + "得到写锁，正在写入。");
            try {
                Thread.sleep(40);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            writeLock.unlock();
            System.err.println(Thread.currentThread().getName() + "释放写锁！");
        }
    }

    public static void main(String[] args) {
        //第一个先是写锁，那么2和3就得排队
        new Thread(() -> write(), "Thread1").start();
        //写锁释放的瞬间，2、3会依次从队列中拿出来，一瞬间的事情（因为2、3阻塞会被唤醒），如果这里瞬间一直存在
        //活跃的线程读那么就可能发生插队的情况，正好就证明了读写锁头结点不是写锁的话，读锁是可以插队的。
        new Thread(() -> read(), "Thread2").start();
        new Thread(() -> read(), "Thread3").start();

        new Thread(() -> write(), "Thread4").start();
        new Thread(() -> read(), "Thread5").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Thread[] threads = new Thread[1000];
                for (int i = 0; i < threads.length; i++) {
                    threads[i] = new Thread(() -> read(), "子线程创建的thread");
                }
                for (int i = 0; i < threads.length; i++) {
                    Thread thread = threads[i];
                    thread.start();
                }
            }
        }).start();
    }
}
